﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using Lilium.Control;
using System.Diagnostics;
using Lilium.Data.Repository;

using Lilium.Web.Mvc.Html;
using Lilium.Web.Mvc.State;

namespace Lilium.Web.Mvc
{
	public class EntityWorkflow
	{
		private const string SavedModelStateTempDataKey = "__ModelState";

		public EntityWorkflow(EntityController controller)
		{
			if (controller == null) throw new ArgumentNullException("controller");

			m_Controller = controller;
			m_ControllerState = controller.GetControllerState();
		}

		public ActionResult GetActionResult()
		{
			ActionResult result;
			if (!ModelState.IsValid)
			{
				result = FailureResult;
			}
			else
				result = SuccessResult;

			if (result is RedirectToRouteResult)
			{	// Это используется, чтобы сохранить информацию об ошибке, которая привела к перенаправлению
				if (!ModelState.IsValid)
					TempData.Add(SavedModelStateTempDataKey, ModelState);
			}
			else
			{	// если результат запроса не перенаправление, поднимаем информацию об ошибках из сохраненного состояния,
				// но делаем это в конце процесса, т.к. на самом деле мы не хотим прерывать процесс
				var savedState = (ModelStateDictionary)TempData[SavedModelStateTempDataKey];
				if (savedState != null)
				{
					TempData.Remove(SavedModelStateTempDataKey);
					foreach (var pair in savedState)
						foreach (var error in pair.Value.Errors)
							ModelState.AddModelError(pair.Key, error.ErrorMessage);
				}
			}

			SaveServicesState();

			return result ?? new ViewResult
			{
				//ViewName = viewName, 
				//MasterName = Controller.masterName, 
				ViewData = Controller.ViewData,
				TempData = Controller.TempData
			};
		}

		public ModelMetadata GetEntityMetadata(Type entityType)
		{
			return ModelMetadataProviders.Current.GetMetadataForType(() => null, entityType);
		}

		[Obsolete("Использовать метод контроллера")]
		public TService GetEntityService<TService>(Type entityType, Func<TService> serviceProvider)
		{ 
			if (entityType == null) throw new ArgumentNullException("entityType");
			if (serviceProvider == null) throw new ArgumentNullException("serviceProvider");

			object serviceObject;
			if (!EntityServices.TryGetValue(entityType, out serviceObject))
			{
				serviceObject = serviceProvider();
				var consumeControlState = serviceObject as IStatefulObject;
				if (consumeControlState != null)
				{
					var key = new EntityServiceStateKey(entityType, consumeControlState.Key);
					object serviceState;
					if (ControllerState.TryGetValue(key, out serviceState))
						consumeControlState.SetState(serviceState);
				}
				EntityServices[entityType] = serviceObject;
			}
			return (TService)serviceObject;
		}

		public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
		{
			return Controller.GetRepository<TEntity>();
		}

		#region public EntityController Controller

		private readonly EntityController m_Controller;

		public EntityController Controller
		{
			//[System.Diagnostics.DebuggerNonUserCodeAttribute]
			get
			{
				return m_Controller;
			}
		}

		#endregion

		#region public Type EntityType

		private readonly Type m_EntityType;

		[Obsolete("Это было нужно для сортировки, сейчас следует переложить эту работу на специальную службу")]
		public Type EntityType
		{
			get
			{
				return Controller.EntityType;
			}
		}

		#endregion

		#region public ActionResult FailureResult

		private ActionResult m_FailureResult;

		public ActionResult FailureResult
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return m_FailureResult;
			}
			[DebuggerNonUserCodeAttribute]
			set
			{
				m_FailureResult = value;
			}
		}

		#endregion

		#region public IDictionary<string, object> ControllerState

		private readonly IDictionary<object, object> m_ControllerState;

		public IDictionary<object, object> ControllerState
		{
			//[DebuggerNonUserCodeAttribute]
			get
			{
				return m_ControllerState;
			}
		}

		#endregion

		#region public object Model

		public object Model
		{
			//[DebuggerNonUserCodeAttribute]
			get
			{
				return Controller.ViewData.Model;
			}
			//[DebuggerNonUserCodeAttribute]
			set
			{
				Controller.ViewData.Model = value;
			}
		}

		#endregion

		#region public ModelStateDictionary ModelState

		public ModelStateDictionary ModelState
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return Controller.ModelState;
			}
		}

		#endregion

		#region public ActionResult SuccessResult

		private ActionResult m_SuccessResult;

		public ActionResult SuccessResult
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return m_SuccessResult;
			}
			[DebuggerNonUserCodeAttribute]
			set
			{
				m_SuccessResult = value;
			}
		}

		#endregion

		#region public TempDataDictionary TempData

		public TempDataDictionary TempData
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return Controller.TempData;
			}
		}

		#endregion

		#region public ViewDataDictionary ViewData

		public ViewDataDictionary ViewData
		{
			[DebuggerNonUserCodeAttribute]
			get
			{
				return Controller.ViewData;
			}
		}

		#endregion

		private void SaveServicesState()
		{
			foreach (var pair in EntityServices)
			{
				var consumeControlState = pair.Value as IStatefulObject;

				if (consumeControlState != null)
				{
                    var state = consumeControlState.GetState(null);
					if (state != null)
					{
						var key = new EntityServiceStateKey(pair.Key, consumeControlState.Key);
						ControllerState[key] = state;
					}
				}
			}
		}

		#region private IDictionary<Type, object> EntityServices

		private readonly Dictionary<Type, object> m_EntityServices = new Dictionary<Type, object>();

		private Dictionary<Type, object> EntityServices
		{
			get
			{
				return m_EntityServices;
			}
		}

		#endregion

		#region private sealed class EntityServiceStateKey

		private sealed class EntityServiceStateKey : IEquatable<EntityServiceStateKey>
		{
			public EntityServiceStateKey(Type entityType, object serviceKey)
			{
				if (entityType == null) throw new ArgumentNullException("entityType");
				if (serviceKey == null) throw new ArgumentNullException("serviceKey");

				m_EntityType = entityType;
				m_ServiceKey = serviceKey;
			}

			public override bool Equals(object obj)
			{
				if (obj is EntityServiceStateKey)
					return Equals((EntityServiceStateKey)obj);
				else
					return false;
			}

			public override int GetHashCode()
			{
				return EntityType.GetHashCode() ^ ServiceKey.GetHashCode();
			}

			#region IEquatable<EntityServiceStateKey > Members

			public bool Equals(EntityServiceStateKey other)
			{
				return this.EntityType == other.EntityType && this.ServiceKey == other.ServiceKey;
			}

			#endregion

			#region public Type EntityType

			private readonly Type m_EntityType;

			public Type EntityType
			{
				get
				{
					return m_EntityType;
				}
			}

			#endregion

			#region public object ServiceKey

			private readonly object m_ServiceKey;

			public object ServiceKey
			{
				get
				{
					return m_ServiceKey;
				}
			}

			#endregion
		}

		#endregion
	}
}